﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Core.Service;
using Microsoft.AspNetCore.Http;
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using Mapster;
using SqlSugar;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Admin.NET.Application.Entity;
namespace Admin.NET.Application;

/// <summary>
/// 区域预算配置表服务 🧩
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupNameStore, Order = 100)]
public partial class StoreBudgetConfigService : IDynamicApiController, ITransient
{
    private readonly SqlSugarRepository<StoreBudgetConfig> _storeBudgetConfigRep;
    private readonly ISqlSugarClient _sqlSugarClient;

    public StoreBudgetConfigService(SqlSugarRepository<StoreBudgetConfig> storeBudgetConfigRep, ISqlSugarClient sqlSugarClient)
    {
        _storeBudgetConfigRep = storeBudgetConfigRep;
        _sqlSugarClient = sqlSugarClient;
    }

    /// <summary>
    /// 分页查询区域预算配置表 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("分页查询区域预算配置表")]
    [ApiDescriptionSettings(Name = "Page"), HttpPost]
    public async Task<SqlSugarPagedList<StoreBudgetConfigOutput>> Page(PageStoreBudgetConfigInput input)
    {
        input.Keyword = input.Keyword?.Trim();
        input.BudgetName = input.BudgetName?.Trim();
        var query = _storeBudgetConfigRep.AsQueryable()
            .WhereIF(input.OrgsId != null, u => u.OrgsId == input.OrgsId)
            .WhereIF(!string.IsNullOrEmpty(input.BudgetName), u => u.BudgetName.Contains(input.BudgetName))
            .LeftJoin<SysOrg>((u, orgs) => u.OrgsId == orgs.Id)
            .Select((u, orgs) => new StoreBudgetConfigOutput
            {
                Id = u.Id,
                OrgsId = u.OrgsId,
                OrgsFkDisplayName = orgs.Name,
                StartDate = u.StartDate,
                EndDate = u.EndDate,
                TotalAmount = u.TotalAmount,
                UsedAmount = u.UsedAmount,
                PendingAmount = u.PendingAmount,
                WarningThreshold = u.WarningThreshold,
                BudgetName = u.BudgetName,
                IsWarningEnabled = u.IsWarningEnabled,
                Remark = u.Remark,
                TenantId = u.TenantId,
                IsDelete = u.IsDelete,
                CreateTime = u.CreateTime,
                UpdateTime = u.UpdateTime,
                CreateUserId = u.CreateUserId,
                CreateUserName = u.CreateUserName,
                UpdateUserId = u.UpdateUserId,
                UpdateUserName = u.UpdateUserName,
            });
		return await query.OrderBuilder(input, "u.").ToPagedListAsync(input.Page, input.PageSize);
    }

    /// <summary>
    /// 获取区域预算配置表详情 ℹ️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("获取区域预算配置表详情")]
    [ApiDescriptionSettings(Name = "Detail"), HttpGet]
    public async Task<StoreBudgetConfig> Detail([FromQuery] QueryByIdStoreBudgetConfigInput input)
    {
        return await _storeBudgetConfigRep.GetFirstAsync(u => u.Id == input.Id);
    }

    /// <summary>
    /// 增加区域预算配置表 ➕
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("增加区域预算配置表")]
    [ApiDescriptionSettings(Name = "Add"), HttpPost]
    public async Task<long> Add(AddStoreBudgetConfigInput input)
    {
        // 数据验证
        if (input.EndDate <= input.StartDate)
            throw Oops.Oh("预算结束时间必须大于开始时间");
            
        if (input.TotalAmount <= 0)
            throw Oops.Oh("总预算额度必须大于0");

        // 检查同一区域是否存在重叠的预算期间
        var overlappingBudgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.OrgsId == input.OrgsId 
                     && u.IsDelete == false
                     && ((input.StartDate >= u.StartDate && input.StartDate < u.EndDate)
                         || (input.EndDate > u.StartDate && input.EndDate <= u.EndDate)
                         || (input.StartDate <= u.StartDate && input.EndDate >= u.EndDate)))
            .CountAsync();

        if (overlappingBudgets > 0)
            throw Oops.Oh("该区域在此时间段内已存在预算配置");

        var entity = input.Adapt<StoreBudgetConfig>();
        return await _storeBudgetConfigRep.InsertAsync(entity) ? entity.Id : 0;
    }

    /// <summary>
    /// 更新区域预算配置表 ✏️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("更新区域预算配置表")]
    [ApiDescriptionSettings(Name = "Update"), HttpPost]
    public async Task Update(UpdateStoreBudgetConfigInput input)
    {
        // 数据验证
        if (input.EndDate <= input.StartDate)
            throw Oops.Oh("预算结束时间必须大于开始时间");
            
        if (input.TotalAmount <= 0)
            throw Oops.Oh("总预算额度必须大于0");

        // 检查同一区域是否存在重叠的预算期间
        var overlappingBudgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.OrgsId == input.OrgsId 
                     && u.Id != input.Id
                     && u.IsDelete == false
                     && ((input.StartDate >= u.StartDate && input.StartDate < u.EndDate)
                         || (input.EndDate > u.StartDate && input.EndDate <= u.EndDate)
                         || (input.StartDate <= u.StartDate && input.EndDate >= u.EndDate)))
            .CountAsync();

        if (overlappingBudgets > 0)
            throw Oops.Oh("该区域在此时间段内已存在预算配置");

        var entity = input.Adapt<StoreBudgetConfig>();
        await _storeBudgetConfigRep.AsUpdateable(entity)
        .IgnoreColumns(u => new {
            u.UsedAmount,
            u.PendingAmount,
        })
        .ExecuteCommandAsync();
    }

    /// <summary>
    /// 删除区域预算配置表 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("删除区域预算配置表")]
    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
    public async Task Delete(DeleteStoreBudgetConfigInput input)
    {
        var entity = await _storeBudgetConfigRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        await _storeBudgetConfigRep.FakeDeleteAsync(entity);   //假删除
        //await _storeBudgetConfigRep.DeleteAsync(entity);   //真删除
    }

    /// <summary>
    /// 批量删除区域预算配置表 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("批量删除区域预算配置表")]
    [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
    public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<DeleteStoreBudgetConfigInput> input)
    {
        var exp = Expressionable.Create<StoreBudgetConfig>();
        foreach (var row in input) exp = exp.Or(it => it.Id == row.Id);
        var list = await _storeBudgetConfigRep.AsQueryable().Where(exp.ToExpression()).ToListAsync();
   
        return await _storeBudgetConfigRep.FakeDeleteAsync(list);   //假删除
        //return await _storeBudgetConfigRep.DeleteAsync(list);   //真删除
    }
    
    /// <summary>
    /// 获取下拉列表数据 🔖
    /// </summary>
    /// <returns></returns>
    [DisplayName("获取下拉列表数据")]
    [ApiDescriptionSettings(Name = "DropdownData"), HttpPost]
    public async Task<Dictionary<string, dynamic>> DropdownData(DropdownDataStoreBudgetConfigInput input)
    {
        var orgsIdData = await _storeBudgetConfigRep.Context.Queryable<SysOrg>()
            .InnerJoinIF<StoreBudgetConfig>(input.FromPage, (u, r) => u.Id == r.OrgsId)
            .Select(u => new {
                Value = u.Id,
                Label = $"{u.Name}"
            }).ToListAsync();
        return new Dictionary<string, dynamic>
        {
            { "orgsId", orgsIdData },
        };
    }
    
    /// <summary>
    /// 导出区域预算配置表记录 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("导出区域预算配置表记录")]
    [ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify]
    public async Task<IActionResult> Export(PageStoreBudgetConfigInput input)
    {
        var list = (await Page(input)).Items?.Adapt<List<ExportStoreBudgetConfigOutput>>() ?? new();
        if (input.SelectKeyList?.Count > 0) list = list.Where(x => input.SelectKeyList.Contains(x.Id)).ToList();
        return ExcelHelper.ExportTemplate(list, "区域预算配置表导出记录");
    }
    
    /// <summary>
    /// 下载区域预算配置表数据导入模板 ⬇️
    /// </summary>
    /// <returns></returns>
    [DisplayName("下载区域预算配置表数据导入模板")]
    [ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
    public IActionResult DownloadTemplate()
    {
        return ExcelHelper.ExportTemplate(new List<ExportStoreBudgetConfigOutput>(), "区域预算配置表导入模板", (_, info) =>
        {
            if (nameof(ExportStoreBudgetConfigOutput.OrgsFkDisplayName) == info.Name) return _storeBudgetConfigRep.Context.Queryable<SysOrg>().Select(u => $"{u.Name}").Distinct().ToList();
            return null;
        });
    }
    
    private static readonly object _storeBudgetConfigImportLock = new object();
    /// <summary>
    /// 导入区域预算配置表记录 💾
    /// </summary>
    /// <returns></returns>
    [DisplayName("导入区域预算配置表记录")]
    [ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
    public IActionResult ImportData([Required] IFormFile file)
    {
        lock (_storeBudgetConfigImportLock)
        {
            var stream = ExcelHelper.ImportData<ImportStoreBudgetConfigInput, StoreBudgetConfig>(file, (list, markerErrorAction) =>
            {
                _sqlSugarClient.Utilities.PageEach(list, 2048, pageItems =>
                {
                    // 链接 区域ID
                    var orgsIdLabelList = pageItems.Where(x => x.OrgsFkDisplayName != null).Select(x => x.OrgsFkDisplayName).Distinct().ToList();
                    if (orgsIdLabelList.Any()) {
                        var orgsIdLinkMap = _storeBudgetConfigRep.Context.Queryable<SysOrg>().Where(u => orgsIdLabelList.Contains($"{u.Name}")).ToList().ToDictionary(u => $"{u.Name}", u => u.Id  as long?);
                        pageItems.ForEach(e => {
                            e.OrgsId = orgsIdLinkMap.GetValueOrDefault(e.OrgsFkDisplayName ?? "");
                            if (e.OrgsId == null) e.Error = "区域ID链接失败";
                        });
                    }
                    
                    // 校验并过滤必填基本类型为null的字段
                    var rows = pageItems.Where(x => {
                        if (!string.IsNullOrWhiteSpace(x.Error)) return false;
                        if (x.OrgsId == null){
                            x.Error = "区域ID不能为空";
                            return false;
                        }
                        return true;
                    }).Adapt<List<StoreBudgetConfig>>();
                    
                    var storageable = _storeBudgetConfigRep.Context.Storageable(rows)
                        .SplitError(it => it.Item.BudgetName?.Length > 128, "预算名称长度不能超过128个字符")
                        .SplitError(it => it.Item.Remark?.Length > 256, "备注长度不能超过256个字符")
                        .SplitInsert(_=> true) // 没有设置唯一键代表插入所有数据
                        .ToStorage();
                    
                    storageable.AsInsertable.ExecuteCommand();// 不存在插入
                    storageable.AsUpdateable.UpdateColumns(it => new
                    {
                        it.OrgsId,
                        it.StartDate,
                        it.EndDate,
                        it.TotalAmount,
                        it.BudgetName,
                        it.Remark,
                    }).ExecuteCommand();// 存在更新
                    
                    // 标记错误信息
                    markerErrorAction.Invoke(storageable, pageItems, rows);
                });
            });
            
            return stream;
        }
    }

    #region 预算额度计算方法

    /// <summary>
    /// 获取指定组织及其所有下级组织的ID列表
    /// </summary>
    /// <param name="orgsId">组织ID</param>
    /// <returns>包含指定组织及其所有下级组织的ID列表</returns>
    private async Task<List<long>> GetOrgAndChildrenIdsAsync(long orgsId)
    {
        var orgIds = new List<long> { orgsId };
        
        // 递归获取所有下级组织
        await GetChildrenOrgIdsAsync(orgsId, orgIds);
        
        return orgIds;
    }

    /// <summary>
    /// 递归获取下级组织ID
    /// </summary>
    /// <param name="parentId">父组织ID</param>
    /// <param name="orgIds">组织ID列表（引用传递）</param>
    private async Task GetChildrenOrgIdsAsync(long parentId, List<long> orgIds)
    {
        // 查询所有下级组织
        var children = await _sqlSugarClient.Queryable<SysOrg>()
            .Where(x => x.Pid == parentId && x.Status == StatusEnum.Enable)
            .Select(x => x.Id)
            .ToListAsync();

        if (children != null && children.Any())
        {
            foreach (var childId in children)
            {
                orgIds.Add(childId);
                // 递归获取下级的下级组织
                await GetChildrenOrgIdsAsync(childId, orgIds);
            }
        }
    }

    /// <summary>
    /// 计算指定组织在指定时间范围内的预算使用情况（包含下级组织）
    /// </summary>
    /// <param name="orgsId">组织ID</param>
    /// <param name="startDate">开始时间</param>
    /// <param name="endDate">结束时间</param>
    /// <returns>已使用额度和申请中额度</returns>
    private async Task<(decimal usedAmount, decimal pendingAmount)> CalculateBudgetUsageAsync(long orgsId, DateTime startDate, DateTime endDate)
    {
        // 获取该组织及其所有下级组织的ID列表
        var orgIds = await GetOrgAndChildrenIdsAsync(orgsId);

        // 计算已使用额度：基于已完成的出库申请（包含下级组织）
        var usedAmount = await _sqlSugarClient.Queryable<StoreOutbound>()
            .Where(x => orgIds.Contains(x.OrgId)
                     && x.CompletedTime >= startDate 
                     && x.CompletedTime <= endDate
                     && x.ApprovalAction == StatusApprovalEnum.Completed
                     && x.CompletedTime != null)
            .SumAsync(x => x.MaterialPriceTotal ?? 0);

        // 计算申请中额度：基于审批中的出库申请（包含下级组织）
        var pendingAmount = await _sqlSugarClient.Queryable<StoreOutbound>()
            .Where(x => orgIds.Contains(x.OrgId)
                     && x.RequestTime >= startDate 
                     && x.RequestTime <= endDate
                     && (x.ApprovalAction == StatusApprovalEnum.Pending 
                         || x.ApprovalAction == StatusApprovalEnum.Reviewed
                         || x.ApprovalAction == StatusApprovalEnum.Approved)
                     && x.CompletedTime == null)
            .SumAsync(x => x.MaterialPriceTotal ?? 0);

        return (usedAmount, pendingAmount);
    }

    /// <summary>
    /// 更新指定预算配置的使用情况
    /// </summary>
    /// <param name="budgetId">预算配置ID</param>
    [DisplayName("更新预算使用情况")]
    [ApiDescriptionSettings(Name = "UpdateBudgetUsage"), HttpPost]
    public async Task UpdateBudgetUsage(long budgetId)
    {
        var budget = await _storeBudgetConfigRep.GetFirstAsync(u => u.Id == budgetId);
        if (budget == null) return;

        var (usedAmount, pendingAmount) = await CalculateBudgetUsageAsync(budget.OrgsId, budget.StartDate, budget.EndDate);

        await _storeBudgetConfigRep.AsUpdateable()
            .SetColumns(u => u.UsedAmount == usedAmount)
            .SetColumns(u => u.PendingAmount == pendingAmount)
            .Where(u => u.Id == budgetId)
            .ExecuteCommandAsync();
    }

    /// <summary>
    /// 更新指定组织的所有有效预算使用情况
    /// </summary>
    /// <param name="orgsId">组织ID</param>
    [DisplayName("更新组织预算使用情况")]
    [ApiDescriptionSettings(Name = "UpdateBudgetByOrg"), HttpPost]
    public async Task UpdateBudgetByOrg(long orgsId)
    {
        var currentDate = DateTime.Now;
        var budgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.OrgsId == orgsId 
                     && u.StartDate <= currentDate 
                     && u.EndDate >= currentDate
                     && u.IsDelete == false)
            .ToListAsync();

        foreach (var budget in budgets)
        {
            var (usedAmount, pendingAmount) = await CalculateBudgetUsageAsync(budget.OrgsId, budget.StartDate, budget.EndDate);

            await _storeBudgetConfigRep.AsUpdateable()
                .SetColumns(u => u.UsedAmount == usedAmount)
                .SetColumns(u => u.PendingAmount == pendingAmount)
                .Where(u => u.Id == budget.Id)
                .ExecuteCommandAsync();
        }
    }

    /// <summary>
    /// 批量更新所有有效预算的使用情况
    /// </summary>
    [DisplayName("批量更新所有预算使用情况")]
    [ApiDescriptionSettings(Name = "UpdateAllBudgetUsage"), HttpPost]
    public async Task UpdateAllBudgetUsage()
    {
        var currentDate = DateTime.Now;
        var budgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.StartDate <= currentDate 
                     && u.EndDate >= currentDate
                     && u.IsDelete == false)
            .ToListAsync();

        foreach (var budget in budgets)
        {
            var (usedAmount, pendingAmount) = await CalculateBudgetUsageAsync(budget.OrgsId, budget.StartDate, budget.EndDate);

            await _storeBudgetConfigRep.AsUpdateable()
                .SetColumns(u => u.UsedAmount == usedAmount)
                .SetColumns(u => u.PendingAmount == pendingAmount)
                .Where(u => u.Id == budget.Id)
                .ExecuteCommandAsync();
        }
    }

    /// <summary>
    /// 检查预算预警
    /// </summary>
    /// <returns>预警列表</returns>
    [DisplayName("检查预算预警")]
    [ApiDescriptionSettings(Name = "CheckBudgetWarnings"), HttpGet]
    public async Task<List<BudgetWarningOutput>> CheckBudgetWarnings()
    {
        var currentDate = DateTime.Now;
        var budgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.StartDate <= currentDate 
                     && u.EndDate >= currentDate
                     && u.IsWarningEnabled == true
                     && u.IsDelete == false)
            .LeftJoin<SysOrg>((u, org) => u.OrgsId == org.Id)
            .Select((u, org) => new 
            {
                u.Id,
                u.BudgetName,
                u.OrgsId,
                OrgName = org.Name,
                u.TotalAmount,
                u.UsedAmount,
                u.PendingAmount,
                u.WarningThreshold,
                u.StartDate,
                u.EndDate
            })
            .ToListAsync();

        var warnings = new List<BudgetWarningOutput>();

        foreach (var budget in budgets)
        {
            var totalUsed = budget.UsedAmount + budget.PendingAmount;
            var usageRate = budget.TotalAmount > 0 ? (totalUsed / budget.TotalAmount) * 100 : 0;

            if (usageRate >= budget.WarningThreshold)
            {
                warnings.Add(new BudgetWarningOutput
                {
                    BudgetId = budget.Id,
                    BudgetName = budget.BudgetName,
                    OrgsId = budget.OrgsId,
                    OrgName = budget.OrgName,
                    TotalAmount = budget.TotalAmount,
                    UsedAmount = budget.UsedAmount,
                    PendingAmount = budget.PendingAmount,
                    TotalUsed = totalUsed,
                    UsageRate = usageRate,
                    WarningThreshold = budget.WarningThreshold,
                    RemainingAmount = budget.TotalAmount - totalUsed,
                    StartDate = budget.StartDate,
                    EndDate = budget.EndDate
                });
            }
        }

        return warnings;
    }

    /// <summary>
    /// 手动刷新指定预算
    /// </summary>
    /// <param name="input">预算ID</param>
    [DisplayName("手动刷新预算")]
    [ApiDescriptionSettings(Name = "RefreshBudget"), HttpPost]
    public async Task RefreshBudget(RefreshBudgetInput input)
    {
        await UpdateBudgetUsage(input.Id);
    }

    /// <summary>
    /// 手动刷新所有预算
    /// </summary>
    [DisplayName("手动刷新所有预算")]
    [ApiDescriptionSettings(Name = "RefreshAllBudgets"), HttpPost]
    public async Task RefreshAllBudgets()
    {
        await UpdateAllBudgetUsage();
    }

    #endregion

    #region 精准时间段匹配方法

    /// <summary>
    /// 获取当前有效的预算配置（精准匹配逻辑）
    /// </summary>
    /// <param name="targetDate">目标日期</param>
    /// <returns>当前有效的预算配置列表</returns>
    private async Task<List<BudgetSummaryOutput>> GetCurrentValidBudgets(DateTime targetDate)
    {
        // 获取所有可能匹配的预算配置
        var allPossibleBudgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.StartDate <= targetDate 
                     && u.EndDate >= targetDate
                     && u.IsDelete == false)
            .LeftJoin<SysOrg>((u, org) => u.OrgsId == org.Id)
            .Select((u, org) => new BudgetSummaryOutput
            {
                Id = u.Id,
                BudgetName = u.BudgetName,
                OrgsId = u.OrgsId,
                OrgName = org.Name,
                TotalAmount = u.TotalAmount,
                UsedAmount = u.UsedAmount,
                PendingAmount = u.PendingAmount,
                WarningThreshold = u.WarningThreshold,
                IsWarningEnabled = u.IsWarningEnabled,
                StartDate = u.StartDate,
                EndDate = u.EndDate
            })
            .ToListAsync();

        // 按区域分组，应用精准匹配逻辑
        var validBudgets = new List<BudgetSummaryOutput>();
        
        var budgetGroups = allPossibleBudgets.GroupBy(b => b.OrgsId);
        
        foreach (var group in budgetGroups)
        {
            // 精准匹配规则：
            // 1. 时间跨度最小的配置（更精确）
            // 2. 开始时间最晚的配置（最新配置）
            // 3. ID最大的配置（创建时间最晚）
            var bestMatch = group
                .OrderBy(b => (b.EndDate - b.StartDate).TotalDays) // 时间跨度最小优先
                .ThenByDescending(b => b.StartDate) // 开始时间最晚优先
                .ThenByDescending(b => b.Id) // ID最大优先
                .FirstOrDefault();

            if (bestMatch != null)
            {
                validBudgets.Add(bestMatch);
            }
        }

        return validBudgets;
    }

    /// <summary>
    /// 获取指定区域在指定日期的有效预算
    /// </summary>
    /// <param name="orgId">区域ID</param>
    /// <param name="targetDate">目标日期</param>
    /// <returns>有效预算配置</returns>
    [DisplayName("获取区域当前预算")]
    [ApiDescriptionSettings(Name = "GetCurrentBudgetByOrg"), HttpGet]
    public async Task<BudgetSummaryOutput?> GetCurrentBudgetByOrg(long orgId, DateTime? targetDate = null)
    {
        var date = targetDate ?? DateTime.Now;
        
        var budgets = await GetCurrentValidBudgets(date);
        return budgets.FirstOrDefault(b => b.OrgsId == orgId);
    }

    /// <summary>
    /// 获取审批用的区域预算信息（支持组织层级继承）
    /// </summary>
    /// <param name="orgId">区域ID</param>
    /// <returns>预算信息</returns>
    [DisplayName("获取审批用区域预算")]
    [ApiDescriptionSettings(Name = "GetBudgetForApproval"), HttpGet]
    public async Task<BudgetSummaryOutput?> GetBudgetForApproval(long orgId)
    {
        var currentDate = DateTime.Now;
        
        // 直接使用现有的方法获取所有有效预算
        var budgets = await GetCurrentValidBudgets(currentDate);
        
        // 先查找当前组织的预算
        var budget = budgets.FirstOrDefault(b => b.OrgsId == orgId);
        
        // 如果当前组织没有预算，查找父级组织的预算
        if (budget == null)
        {
            var parentOrgId = await GetParentOrgId(orgId);
            if (parentOrgId.HasValue)
            {
                budget = budgets.FirstOrDefault(b => b.OrgsId == parentOrgId.Value);
                
                // 如果还是没有，继续向上查找
                if (budget == null)
                {
                    var grandParentOrgId = await GetParentOrgId(parentOrgId.Value);
                    if (grandParentOrgId.HasValue)
                    {
                        budget = budgets.FirstOrDefault(b => b.OrgsId == grandParentOrgId.Value);
                    }
                }
            }
        }

        // 如果找到预算，计算使用率和剩余额度
        if (budget != null)
        {
            var totalUsed = budget.UsedAmount + budget.PendingAmount;
            budget.TotalUsed = totalUsed;
            budget.UsageRate = budget.TotalAmount > 0 ? (totalUsed / budget.TotalAmount) * 100 : 0;
            budget.RemainingAmount = budget.TotalAmount - totalUsed;
        }

        return budget;
    }

    /// <summary>
    /// 获取父级组织ID
    /// </summary>
    private async Task<long?> GetParentOrgId(long orgId)
    {
        var currentOrg = await _sqlSugarClient.Queryable<SysOrg>()
            .Where(x => x.Id == orgId)
            .FirstAsync();
            
        if (currentOrg == null || currentOrg.Pid == 0) return null;

        return currentOrg.Pid;
    }

    /// <summary>
    /// 获取区域预算历史配置
    /// </summary>
    /// <param name="orgId">区域ID</param>
    /// <returns>预算历史配置</returns>
    [DisplayName("获取区域预算历史")]
    [ApiDescriptionSettings(Name = "GetBudgetHistory"), HttpGet]
    public async Task<List<BudgetHistoryOutput>> GetBudgetHistory(long orgId)
    {
        var budgets = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.OrgsId == orgId && u.IsDelete == false)
            .LeftJoin<SysOrg>((u, org) => u.OrgsId == org.Id)
            .OrderByDescending(u => u.StartDate)
            .Select((u, org) => new BudgetHistoryOutput
            {
                Id = u.Id,
                BudgetName = u.BudgetName,
                OrgsId = u.OrgsId,
                OrgName = org.Name,
                TotalAmount = u.TotalAmount,
                UsedAmount = u.UsedAmount,
                PendingAmount = u.PendingAmount,
                StartDate = u.StartDate,
                EndDate = u.EndDate,
                IsActive = u.StartDate <= DateTime.Now && u.EndDate >= DateTime.Now,
                CreateTime = u.CreateTime
            })
            .ToListAsync();

        return budgets;
    }

    /// <summary>
    /// 检查预算配置是否存在重叠
    /// </summary>
    /// <param name="orgId">区域ID</param>
    /// <param name="startDate">开始日期</param>
    /// <param name="endDate">结束日期</param>
    /// <param name="excludeId">排除的配置ID（用于编辑时检查）</param>
    /// <returns>重叠检查结果</returns>
    [DisplayName("检查预算配置重叠")]
    [ApiDescriptionSettings(Name = "CheckBudgetConflicts"), HttpPost]
    public async Task<BudgetConflictCheckOutput> CheckBudgetConflicts(CheckBudgetConflictInput input)
    {
        var conflicts = await _storeBudgetConfigRep.AsQueryable()
            .Where(u => u.OrgsId == input.OrgsId 
                     && u.IsDelete == false
                     && (input.ExcludeId == null || u.Id != input.ExcludeId)
                     && ((input.StartDate >= u.StartDate && input.StartDate <= u.EndDate)
                         || (input.EndDate >= u.StartDate && input.EndDate <= u.EndDate)
                         || (input.StartDate <= u.StartDate && input.EndDate >= u.EndDate)))
            .LeftJoin<SysOrg>((u, org) => u.OrgsId == org.Id)
            .Select((u, org) => new BudgetConflictInfo
            {
                Id = u.Id,
                BudgetName = u.BudgetName,
                OrgName = org.Name,
                StartDate = u.StartDate,
                EndDate = u.EndDate,
                TotalAmount = u.TotalAmount
            })
            .ToListAsync();

        return new BudgetConflictCheckOutput
        {
            HasConflicts = conflicts.Any(),
            ConflictCount = conflicts.Count,
            Conflicts = conflicts,
            Suggestions = GenerateConflictSuggestions(conflicts, input)
        };
    }

    /// <summary>
    /// 生成冲突解决建议
    /// </summary>
    private List<string> GenerateConflictSuggestions(List<BudgetConflictInfo> conflicts, CheckBudgetConflictInput input)
    {
        var suggestions = new List<string>();

        if (!conflicts.Any()) return suggestions;

        suggestions.Add("发现时间段重叠的预算配置，建议采用以下方案之一：");

        foreach (var conflict in conflicts)
        {
            if (conflict.EndDate < input.StartDate.AddDays(-1))
            {
                suggestions.Add($"• 调整开始时间至 {conflict.EndDate.AddDays(1):yyyy-MM-dd} 之后");
            }
            else if (conflict.StartDate > input.EndDate.AddDays(1))
            {
                suggestions.Add($"• 调整结束时间至 {conflict.StartDate.AddDays(-1):yyyy-MM-dd} 之前");
            }
            else
            {
                suggestions.Add($"• 修改现有配置「{conflict.BudgetName}」的时间范围");
                suggestions.Add($"• 或者删除重叠的配置后重新创建");
            }
        }

        return suggestions;
    }

    #endregion

    #region 看板专用接口

    /// <summary>
    /// 获取看板数据 📊
    /// </summary>
    /// <param name="targetDate">目标日期，不指定则使用当前日期</param>
    /// <returns>看板数据</returns>
    [DisplayName("获取看板数据")]
    [ApiDescriptionSettings(Name = "GetDashboardData"), HttpGet]
    public async Task<BudgetDashboardOutput> GetDashboardData(DateTime? targetDate = null)
    {
        var queryDate = targetDate ?? DateTime.Now;
        
        // 获取指定日期有效的预算配置（使用精准匹配逻辑）
        var budgets = await GetCurrentValidBudgets(queryDate);

        // 计算汇总数据
        var totalAmount = budgets.Sum(b => b.TotalAmount);
        var totalUsed = budgets.Sum(b => b.UsedAmount);
        var totalPending = budgets.Sum(b => b.PendingAmount);
        var totalRemaining = totalAmount - totalUsed - totalPending;

        // 计算每个预算的使用率和状态
        var budgetSummaries = budgets.Select(b =>
        {
            var totalBudgetUsed = b.UsedAmount + b.PendingAmount;
            var usageRate = b.TotalAmount > 0 ? (totalBudgetUsed / b.TotalAmount) * 100 : 0;
            var remaining = b.TotalAmount - totalBudgetUsed;

            return new BudgetSummaryOutput
            {
                Id = b.Id,
                BudgetName = b.BudgetName,
                OrgsId = b.OrgsId,
                OrgName = b.OrgName,
                TotalAmount = b.TotalAmount,
                UsedAmount = b.UsedAmount,
                PendingAmount = b.PendingAmount,
                TotalUsed = totalBudgetUsed,
                UsageRate = usageRate,
                RemainingAmount = remaining,
                WarningThreshold = b.WarningThreshold,
                IsWarningEnabled = b.IsWarningEnabled,
                Status = GetBudgetStatus(usageRate, b.WarningThreshold, b.IsWarningEnabled),
                StartDate = b.StartDate,
                EndDate = b.EndDate
            };
        }).OrderBy(b => b.OrgName).ToList();

        // 统计预警数量
        var warningCount = budgetSummaries.Count(b => b.Status == "warning");
        var dangerCount = budgetSummaries.Count(b => b.Status == "danger");

        return new BudgetDashboardOutput
        {
            Summary = new BudgetOverallSummary
            {
                TotalBudgetCount = budgets.Count,
                TotalAmount = totalAmount,
                TotalUsed = totalUsed,
                TotalPending = totalPending,
                TotalRemaining = totalRemaining,
                OverallUsageRate = totalAmount > 0 ? ((totalUsed + totalPending) / totalAmount) * 100 : 0,
                WarningCount = warningCount,
                DangerCount = dangerCount
            },
            BudgetDetails = budgetSummaries,
            LastUpdateTime = DateTime.Now,
            QueryDate = queryDate // 添加查询日期信息
        };
    }

    /// <summary>
    /// 获取预算状态
    /// </summary>
    /// <param name="usageRate">使用率</param>
    /// <param name="warningThreshold">预警阈值</param>
    /// <param name="isWarningEnabled">是否启用预警</param>
    /// <returns>状态</returns>
    private string GetBudgetStatus(decimal usageRate, decimal warningThreshold, bool isWarningEnabled)
    {
        if (!isWarningEnabled) return "normal";
        
        if (usageRate >= 100) return "danger";
        if (usageRate >= warningThreshold) return "warning";
        return "normal";
    }

    /// <summary>
    /// 获取预算使用趋势数据 📈
    /// </summary>
    /// <param name="days">天数，默认30天</param>
    /// <returns>趋势数据</returns>
    [DisplayName("获取预算使用趋势")]
    [ApiDescriptionSettings(Name = "GetBudgetTrend"), HttpGet]
    public async Task<List<BudgetTrendOutput>> GetBudgetTrend(int days = 30)
    {
        var endDate = DateTime.Now.Date;
        var startDate = endDate.AddDays(-days);
        
        var trendData = new List<BudgetTrendOutput>();
        
        for (var date = startDate; date <= endDate; date = date.AddDays(1))
        {
            // 获取该日期的预算快照数据（这里简化处理，实际可能需要历史数据表）
            var dayData = await _storeBudgetConfigRep.AsQueryable()
                .Where(u => u.StartDate <= date 
                         && u.EndDate >= date
                         && u.IsDelete == false)
                .SumAsync(u => u.UsedAmount);

            trendData.Add(new BudgetTrendOutput
            {
                Date = date,
                TotalUsed = dayData,
                DayOfWeek = date.DayOfWeek.ToString()
            });
        }

        return trendData;
    }

    #endregion
}
